*/ public function register(): array { return [ T_CLASS_C, ...TokenHelper::ONLY_NAME_TOKEN_CODES, ]; } /** * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint * @param int $pointer */ public function process(File $phpcsFile, $pointer): void { $this->enableOnObjects = SniffSettingsHelper::isEnabledByPhpVersion($this->enableOnObjects, 80000); $tokens = $phpcsFile->getTokens(); if ($tokens[$pointer]['code'] === T_CLASS_C) { $this->checkMagicConstant($phpcsFile, $pointer); return; } $this->checkFunctionCall($phpcsFile, $pointer); } private function checkMagicConstant(File $phpcsFile, int $pointer): void { $fix = $phpcsFile->addFixableError( 'Class name referenced via magic constant.', $pointer, self::CODE_CLASS_NAME_REFERENCED_VIA_MAGIC_CONSTANT, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); FixerHelper::replace($phpcsFile, $pointer, 'self::class'); $phpcsFile->fixer->endChangeset(); } private function checkFunctionCall(File $phpcsFile, int $functionPointer): void { $tokens = $phpcsFile->getTokens(); $functionName = ltrim(strtolower($tokens[$functionPointer]['content']), '\\'); $functionNames = [ 'get_class', 'get_parent_class', 'get_called_class', ]; if (!in_array($functionName, $functionNames, true)) { return; } $openParenthesisPointer = TokenHelper::findNextEffective($phpcsFile, $functionPointer + 1); if ($tokens[$openParenthesisPointer]['code'] !== T_OPEN_PARENTHESIS) { return; } $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $functionPointer - 1); if (in_array($tokens[$previousPointer]['code'], [T_OBJECT_OPERATOR, T_DOUBLE_COLON, T_FUNCTION], true)) { return; } $parameterPointer = TokenHelper::findNextEffective( $phpcsFile, $openParenthesisPointer + 1, $tokens[$openParenthesisPointer]['parenthesis_closer'], ); $isObjectParameter = static function () use ($phpcsFile, $tokens, $openParenthesisPointer, $parameterPointer): bool { if ($tokens[$parameterPointer]['code'] !== T_VARIABLE) { return false; } $pointerAfterParameterPointer = TokenHelper::findNextEffective($phpcsFile, $parameterPointer + 1); return $pointerAfterParameterPointer === $tokens[$openParenthesisPointer]['parenthesis_closer']; }; $isThisParameter = static function () use ($tokens, $parameterPointer, $isObjectParameter): bool { if (!$isObjectParameter()) { return false; } $parameterName = strtolower($tokens[$parameterPointer]['content']); return $parameterName === '$this'; }; if ($functionName === 'get_class') { if ($parameterPointer === null) { $fixedContent = 'self::class'; } elseif ($isThisParameter()) { $fixedContent = 'static::class'; } elseif ($this->enableOnObjects && $isObjectParameter()) { $fixedContent = sprintf('%s::class', $tokens[$parameterPointer]['content']); } else { return; } } elseif ($functionName === 'get_parent_class') { if ($parameterPointer !== null) { if (!$isThisParameter()) { return; } $classPointer = FunctionHelper::findClassPointer($phpcsFile, $functionPointer); if ($classPointer === null || !ClassHelper::isFinal($phpcsFile, $classPointer)) { return; } } $fixedContent = 'parent::class'; } else { $fixedContent = 'static::class'; } $fix = $phpcsFile->addFixableError( sprintf('Class name referenced via call of function %s().', $functionName), $functionPointer, self::CODE_CLASS_NAME_REFERENCED_VIA_FUNCTION_CALL, ); if (!$fix) { return; } $phpcsFile->fixer->beginChangeset(); if ($tokens[$functionPointer - 1]['code'] === T_NS_SEPARATOR) { FixerHelper::replace($phpcsFile, $functionPointer - 1, ''); } FixerHelper::change($phpcsFile, $functionPointer, $tokens[$openParenthesisPointer]['parenthesis_closer'], $fixedContent); $phpcsFile->fixer->endChangeset(); } }